<?php

/**
 * Class for handle all information aboult price caculation.
 *
 * @access 	public
 * @author 	Valentin
 * @copyright	ResMania 2009 all rights reserved.
 * @version	1.0
 * @link		http://developer.resmania.com/api
 * @since  	05-2009
 */
class RM_DailyPricesFixed_Calculator {

    /**
     * @var RM_UnitDailyPricesFixed
     */
    protected $_unitPricesModel;
    /**
     * @var RM_UnitDailyPricesFixedConfig
     */
    protected $_unitPricesConfigModel;
    /**
     * @var Zend_Translate
     */
    protected $_translate;

    /**
     * Constructor
     *
     * @param RM_UnitDailyPricesFixed $unitPricesModel
     * @param RM_UnitDailyPricesFixedConfig $unitPricesConfigModel
     * @param Zend_Translate $translate
     */
    public function __construct($unitPricesModel, $unitPricesConfigModel, $translate) {
        $this->setUnitPricesModel($unitPricesModel);
        $this->setUnitPricesConfigModel($unitPricesConfigModel);
        $this->setTranslate($translate);
    }
    /**
     * Gets the unit prices model
     * @return the unit prices model
     */
    protected function getUnitPricesModel() {
        return $this->unitPricesModel;
    }
    /**
     * Sets the unit prices model
     * @param unitPricesModel the unit prices model
     */
    protected function setUnitPricesModel($unitPricesModel) {
        $this->unitPricesModel = $unitPricesModel;
    }
    /**
     * Gets the unit prices config model
     * @return the unit prices config model
     */
    protected function getUnitPricesConfigModel() {
        return $this->unitPricesConfigModel;
    }
    /**
     * Sets the unit prices config model
     * @param unitPricesConfigModel the unit prices config model
     */
    protected function setUnitPricesConfigModel($unitPricesConfigModel) {
        $this->unitPricesConfigModel = $unitPricesConfigModel;
    }
    /**
     * Gets the language translator
     * @return the language translator
     */
    protected function getTranslate() {
        return $this->translate;
    }
    /**
     * Sets the language translator
     * @param translate language translator
     */
    protected function setTranslate($translate) {
        $this->translate = $translate;
    }
    /**
     * Calculate the total price for a given period
     * @param [array] days the set of days composing the period
     * @param [array] model the pricing model
     * @return the total price
     */
    protected function getPeriodPrice($days, $model) {
        $price = 0;
        $daysCount = count($days);
        if ($daysCount>=7) {
            // applies the full-week price
            $price = $daysCount * $model['full_week']/7;
        }
        else if ($daysCount>0) {
            // count the number of weekend days and week days into the period
            $weekendCount = 0;
            $weekCount = 0;
            $dt = null;
            foreach($days as $day) {
                $dt = getdate($day->get(Zend_Date::TIMESTAMP));
                $dayId = $dt['wday'];
                if ($dayId==0 || $dayId==6) {
                    $weekendCount++;
                }
                else {
                    $weekCount++;
                }
            }
            // add the weekend days price
            if ($weekendCount>1) {
                $price += $model['full_weekend'];
            }
            else if ($weekendCount>0) {
                $price += $model['one_weekend_day'];
            }
            // add the midweek days price
            switch($weekCount) {
                case 1:
                    $price += $model['one_week_day'];
                    break;
                case 2:
                    $price += $model['two_week_days'];
                    break;
                case 3:
                    $price += $model['three_week_days'];
                    break;
                case 4:
                    $price += $model['four_week_days'];
                    break;
                case 5:
                    $price += $model['five_week_days'];
                    break;
            }
        }
        return $price;
    }
    /**
     * Check if a given period met reservation constraints on a given pricing model
     * @param [array of RM_Date] list of days in the reservation period
     * @param [array] model the pricing model
     * @throws RM_Exception if the period does not respect one or more contraints
     */
    protected function checkConstraints($days, $model) {
        $minStay = intval($model['min_stay']);
        $startDay = $model['week_startday'];
        $daysCount = count($days);
        // check the reservation length constraints
        if ($minStay>0 && $daysCount<$minStay) {
            $msg = $this->getTranslate()->_('RM.Module.DailyPricesFixed', 'DaysNumberIsSmallerThanMin');
            throw new RM_Exception(str_replace("[XX]", $minStay, $msg));
        }
        // check the first day constraints
        if ($startDay>-1) {
            $dt = getdate($days[0]->get(Zend_Date::TIMESTAMP));
            $dayId = $dt['wday'];
            if ($dayId!=$startDay) {
                $translate = RM_Environment::getInstance()->getTranslation(RM_Environment::TRANSLATE_MAIN);
                $dayJson = $translate->_('Common.JSON', 'WeekDaysLongNumbered');
                $dayJson = str_replace(chr(39), chr(34), $dayJson); //Zend_Json::decode does not support single quotes so replace these with doubles
                $daysArray = Zend_Json::decode($dayJson);
                $msg = $this->getTranslate()->_('RM.Module.DailyPrices', 'StartDayNotAllowed');
                throw new RM_Exception(str_replace("[DAY]", $daysArray[$startDay][1], $msg));
            }
        }
    }
    /**
     * Calculate total price for one unit
     *
     * @param RM_Prices_Information $information
     */
    public function getTotalUnitPrice($information) {
        $unitPrice = 0;
        $unit = $information->getUnit();
        $period = $information->getPeriod();
        $persons = $information->getPersons();
        $pstart = $period->getStart();
        $pend = $period->getEnd();
        // gets the pricing information for this unit
        $pricesModel = $this->getUnitPricesModel();
        $prices = $pricesModel->getByUnit($unit, 'start_datetime');
        $curPeriod = null;
        $priceArr = null;
        $constraintsChecked = false;
        foreach($prices as $idx=>$price) {
            // check if there is an intersection with the current period
            $dtStart = new RM_Date(strtotime($price['start_datetime']));
            $dtStart->set($pstart->get('H'), 'H');
            $dtStart->set($pstart->get('i'), 'i');
            $dtEnd = new RM_Date(strtotime($price['end_datetime']));
            $dtEnd->set($pend->get('H'), 'H');
            $dtEnd->set($pend->get('i'), 'i');
            $sstart = $dtStart->toMySQL();
            $ssend = $dtEnd->toMySQL();
            $curPeriod = new RM_Reservation_Period($dtStart, $dtEnd);
            $pint = $curPeriod->getIntersection($period);
            $days = array();
            if (!empty($pint)) {
                $days = $pint->getDays($dtEnd->isEarlier($pend) ? 0 : 1);
                $priceArr = $price->toArray();
                if (!$constraintsChecked) {
                    // check if reservation constraints are met
                    $this->checkConstraints($days, $priceArr);
                    $constraintsChecked = true;
                }
            }
            else if ((0==$pstart->compareDate($dtEnd)) && $dtEnd->isEarlier($pend)) {
                // reservation start on last day of this period
                $days[] = $dtEnd;
                $constraintsChecked = true;
            }
            if (count($days)>0) {
                // compute price for this set of days
                $priceArr = $price->toArray();
                $periodPrice = $this->getPeriodPrice($days, $priceArr);
                $unitPrice += $periodPrice;
            }
        }
        $totalPeople = $persons->getAdults() + $persons->getChildren() + $persons->getInfants();
        if ( $totalPeople >0 ) {
            $unitPrice *= $totalPeople;
        }
        return RM_Environment::getInstance()->roundPrice($unitPrice);
    }
}